package cn.xinfei.xdecision.engine.runner.service.impl;

import cn.xinfei.xdecision.common.datadao.DecisionResultMapper;
import cn.xinfei.xdecision.common.datamodel.DecisionResult;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.common.utils.util.IpUtil;
import cn.xinfei.xdecision.engine.runner.context.PipelineContext;
import cn.xinfei.xdecision.engine.runner.context.model.TrackModel;
import cn.xinfei.xdecision.engine.runner.service.DecisionResultDetailService;
import cn.xinfei.xdecision.engine.runner.service.MongoTemplateProxy;
import cn.xinfei.xdecision.pulsar.config.XDecisionMessage;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dianping.cat.Cat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;

@Service
@Slf4j
public class DecisionResultDetailServiceImpl extends ServiceImpl<DecisionResultMapper, DecisionResult> implements DecisionResultDetailService {


    @Autowired
    private MongoTemplateProxy mongoTemplateProxy;

    @Value("${xdecision.decisionresult.decisionSnapshotMongoTable:t_decision_result_detail}")
    private String decisionResultTable;

    @Value("${xdecision.decisionresult.nodeLogMongoTable:t_decision_node_log}")
    private String nodeLogTopic;

    @Value("${xdecision.decisionresult.dataxQueryLogMongoTable:t_decision_query_log}")
    private String dataxQueryLogMongoTable;

    @Value("${xdecision.decisionresult.reentryLogMongoTable:t_decision_reentry_log}")
    private String reentryLogMongoTable;

    private volatile int reentryTimeout = 3;

    @Override
    public boolean saveReentryLog(String message) {
        String upsert = mongoTemplateProxy.insertOne(reentryLogMongoTable, message);
        return true;
    }

    @Override
    public boolean saveXDataQueryLog(String message) {
        String upsert = mongoTemplateProxy.insertOne(dataxQueryLogMongoTable, message);
        return true;
    }

    @Override
    public boolean saveEngineNodeLog(String message) {
        String upsert = mongoTemplateProxy.insertOne(nodeLogTopic, message);

        return true;
    }

    @Override
    public boolean saveDecisionResultDetail(String message) {
        String upsert = mongoTemplateProxy.upsertOne(decisionResultTable, message);
//        log.info("insert into mongodb one snapshot,doc={}", upsert);
//        JSONObject jsonObject = JSONObject.parseObject(message);
//        String bizId = jsonObject.getString("requestId");
//        String decisionId = jsonObject.getString("decisionId");
        return true;
    }


    @Override
    public boolean upsertDecisionResultDetail(PipelineContext pipelineContext, String type) {
        boolean resultDetail = false;
        String code = pipelineContext.getCode();
//        if (XDecisionMessage.MsgType.UPDATE_STATE.name().equals(type)) {
//            resultDetail = this.updateDecisionResultState(pipelineContext);
//            log.info("upsertDecisionResultDetail.updateDecisionResultState result={},code={}", resultDetail, code);
//        } else if (XDecisionMessage.MsgType.UPDATE_LOG.name().equals(type)) {
//            resultDetail = this.upsertDecisionResultDetailLog(pipelineContext);
//            log.info("upsertDecisionResultDetail.updateDecisionResultLog result={},code={}", resultDetail, code);
//        } else if (XDecisionMessage.MsgType.UPDATE.name().equals(type)) {
//            resultDetail = this.updateDecisionResultDetail(pipelineContext);
//            log.info("upsertDecisionResultDetail.updateDecisionResultDetail result={},code={}", resultDetail, code);
//        } else if (XDecisionMessage.MsgType.INSERT.name().equals(type)) {
//            resultDetail = this.saveDecisionResultDetail(pipelineContext);
//            log.info("upsertDecisionResultDetail.saveDecisionResultDetail result={},code={}", resultDetail, code);
//        } else {
//            resultDetail = this.saveDecisionResultDetail(pipelineContext);
//            log.info("upsertDecisionResultDetail.saveDecisionResultDetail result={},code={}", resultDetail, code);
//        }
        return resultDetail;
    }

    @Override
    public boolean saveDecisionResultDetail(PipelineContext pipelineContext) {
        String save = mongoTemplateProxy.insertOne(decisionResultTable, JsonUtil.toJson(pipelineContext));
        if (StringUtils.isEmpty(save)) {
            return false;
        }
        return true;
    }

    @Override
    public boolean upsertDecisionResultDetail(PipelineContext pipelineContext) {
//        String upsert = mongoTemplateProxy.upsertOne(decisionResultTable, pipelineContext);
        String engineCode = pipelineContext.getEngineCode();
        String decisionId = pipelineContext.getDecisionId();
        String requestId = pipelineContext.getRequestId();

        String lockKey = "UPDATE" + decisionId + ":" + requestId + ":" + engineCode;
        String ipAddress = IpUtil.getIpAddress();
//        Boolean expire = RedisLock.tryLock(ipAddress, lockKey, reentryTimeout);
        Boolean expire = true;
        if (expire) {
            try {
                //此处存在并发问题 todo shiyusen
                Document upsert = mongoTemplateProxy.findByDecisionId(decisionResultTable, engineCode, decisionId);
                String updateResult = null;
                if (upsert == null) {
                    updateResult = mongoTemplateProxy.insertOne(decisionResultTable, JsonUtil.toJson(pipelineContext));
                } else {
                    updateResult = mongoTemplateProxy.updateOne(decisionResultTable, pipelineContext, upsert);
                }
                if (StringUtils.isEmpty(updateResult)) {
                    return false;
                }
                return true;
            } catch (Exception e) {
                log.error("updateOne接口处理异常，decisionId={},msg={}", e.getMessage(), decisionId, e);
                Cat.logError(e);
            } finally {
//                RedisLock.releaseLock(ipAddress, lockKey);
            }

        }
        log.error("没有获取锁,lockKey={}", lockKey);
        return false;

    }

    @Override
    public boolean updateDecisionResultState(PipelineContext pipelineContext) {
        String engineCode = pipelineContext.getEngineCode();
        String decisionId = pipelineContext.getDecisionId();
        String requestId = pipelineContext.getRequestId();

        Update update = new Update();
        update.set("code", pipelineContext.getCode());
        update.set("state", pipelineContext.getState());
        update.set("result", pipelineContext.getResult());
        update.set("updateTime", DateUtils.getCurrentStrDate());
        update.set("respond", pipelineContext.getRespond());

        update.set("output", pipelineContext.getOutput());
        update.set("context", pipelineContext.getContext());
//        update.push("track", pipelineContext.getTrack());//追加

        ConcurrentHashMap<String, List<TrackModel>> track = pipelineContext.getTrack();
        Set<Map.Entry<String, List<TrackModel>>> entries = track.entrySet();
        Iterator<Map.Entry<String, List<TrackModel>>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<TrackModel>> next = iterator.next();
            String engineCodeTrack = next.getKey();
            List<TrackModel> trackModelList = next.getValue();
//            Update.PushOperatorBuilder pob = update.push("track."+engineCodeTrack);
//            pob.each(trackModelList);


            Update.AddToSetBuilder pob = update.addToSet("track." + engineCodeTrack);
            pob.each(trackModelList);
        }


        update.set("nodeCodeStack", pipelineContext.getNodeCodeStack());
        update.set("hitResultList", pipelineContext.getHitResultList());
        update.set("branchRejectInfo", pipelineContext.getBranchRejectInfo());

        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(requestId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));//为了走分片
        Query query = new Query(criteriaDefinition);
        String s = mongoTemplateProxy.upsertOneDecisionDetail(decisionResultTable, update, query, decisionId, requestId);
        if (StringUtils.isNotEmpty(s)) {
            return true;
        }
        return false;
    }

    public boolean upsertDecisionResultDetailLog(PipelineContext pipelineContext,String executorDecisionId,String updateDecisionId) {
        String engineCode = pipelineContext.getEngineCode();
        String decisionId = pipelineContext.getDecisionId();
        String requestId = pipelineContext.getRequestId();

        Update update = new Update();
        //只插入，不更新
        update.setOnInsert("decisionId", pipelineContext.getDecisionId());
        update.setOnInsert("requestId", pipelineContext.getRequestId());
        update.setOnInsert("engineCode", pipelineContext.getEngineCode());
        update.setOnInsert("engineName", pipelineContext.getEngineName());
        update.setOnInsert("engineVersion", pipelineContext.getEngineVersion());
        update.setOnInsert("scene", pipelineContext.getScene());
        update.setOnInsert("tpCode", pipelineContext.getTpCode());
        update.setOnInsert("parentDecisionId", pipelineContext.getParentDecisionId());
        update.setOnInsert("championDecisionId", pipelineContext.getChampionDecisionId());
        update.setOnInsert("mainDecisionId", pipelineContext.getMainDecisionId());

        update.setOnInsert("createTime", pipelineContext.getCreateTime());
        update.setOnInsert("branch", pipelineContext.getBranch());
        update.setOnInsert("organId", pipelineContext.getOrganId());


        //执行的和更新的都是同一个决策流，则进行update
        if(executorDecisionId.equals(updateDecisionId)){
            update.set("hitResultList", pipelineContext.getHitResultList());
            update.set("branchRejectInfo", pipelineContext.getBranchRejectInfo());
            update.set("output", pipelineContext.getOutput());
            update.set("nodeCodeStack", pipelineContext.getNodeCodeStack());
            update.set("context", pipelineContext.getContext());
            update.set("input", pipelineContext.getInput());

        }else{
            //如果不是同一个，则按照“无则插入，有则忽略”
            //比如执行子决策流的时候，更新父决策流时，这些字段则不必更新
            update.setOnInsert("hitResultList", pipelineContext.getHitResultList());
            update.setOnInsert("branchRejectInfo", pipelineContext.getBranchRejectInfo());
            update.setOnInsert("nodeCodeStack", pipelineContext.getNodeCodeStack());
            //上下文是要更新的
            update.set("context", pipelineContext.getContext());
            //输出要更新
            update.set("output", pipelineContext.getOutput());
        }

        //这个track字段待定
        ConcurrentHashMap<String, List<TrackModel>> track = pipelineContext.getTrack();
        Set<Map.Entry<String, List<TrackModel>>> entries = track.entrySet();
        Iterator<Map.Entry<String, List<TrackModel>>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<TrackModel>> next = iterator.next();
            String engineCodeTrack = next.getKey();
            List<TrackModel> trackModelList = next.getValue();

            Update.AddToSetBuilder pob = update.addToSet("track." + engineCodeTrack);
            pob.each(trackModelList);
        }





        //第一次插入，之后会更新覆盖
        update.set("code", pipelineContext.getCode());
        update.set("state", pipelineContext.getState());
        update.set("result", pipelineContext.getResult());
        update.set("respond", pipelineContext.getRespond());
        update.set("updateTime", DateUtils.getCurrentStrDate());



      Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(requestId)
                .and("engineCode").is(engineCode);
//                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));//为了走分片
        Query query = new Query(criteriaDefinition);
        String s = mongoTemplateProxy.upsertOneDecisionDetail(decisionResultTable, update, query, decisionId, requestId);
        if (StringUtils.isNotEmpty(s)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Map<String,Object> dd=new HashMap<>();
        dd.put("a","\"\"");
        System.out.println(dd.get("a"));
        System.out.println(dd.get("a").equals(""));
        System.out.println(dd.get("a").equals("\"\""));
    }
    @Override
    public boolean updateDecisionResultDetail(PipelineContext pipelineContext) {
        String engineCode = pipelineContext.getEngineCode();
        String decisionId = pipelineContext.getDecisionId();
        String requestId = pipelineContext.getRequestId();

        Update update = Update.fromDocument(Document.parse(JsonUtil.toJson(pipelineContext)));

        Criteria criteriaDefinition = Criteria
                .where("decisionId").is(decisionId)
                .and("requestId").is(requestId)
                .and("engineCode").is(engineCode)
                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));//为了走分片
        Query query = new Query(criteriaDefinition);
        String s = mongoTemplateProxy.upsertOneDecisionDetail(decisionResultTable, update, query, requestId, decisionId);
        if (StringUtils.isNotEmpty(s)) {
            return true;
        }
        return false;
    }


    @Override
    public PipelineContext getDecisionResultDetailByDecisionId(String bizId, String decisionId, String engineCode) {
        PipelineContext jsonObjects = mongoTemplateProxy.findByDecisionId(decisionResultTable, engineCode, bizId, decisionId);
        return jsonObjects;
    }

    @Override
    public List<Document> getDecisionResultDetailByBizId(String bizId, String engineCode) {
        List<Document> jsonObjects = mongoTemplateProxy.findByBizId(decisionResultTable, engineCode, bizId);
        return jsonObjects;
    }

    @Override
    public Document getDecisionResultDetailByDecisionId(String decisionId, String engineCode) {
        Document jsonObjects = mongoTemplateProxy.findByDecisionId(decisionResultTable, engineCode, decisionId);
        return jsonObjects;
    }

    @Override
    public int updateDecisionResultDetailStateByBizId(DecisionResult decisionResult) {
        Calendar instance = Calendar.getInstance();
//        instance.add(Calendar.DAY_OF_MONTH, -7);
//        String decisionId = decisionResult.getDecisionId();
//        String requestId = decisionResult.getRequestId();
//        String engineCode = decisionResult.getEngineCode();
//
//        Update update = Update.fromDocument(doc, "");
//        Criteria criteriaDefinition = Criteria
//                .where("decisionId").is(decisionId)
//                .and("requestId").is(requestId)
//                .and("engineCode").is(engineCode)
//                .and("createTime").gt(DateUtils.getBeforeNumDayDate(1));//为了走分片
//        Query query = new Query(criteriaDefinition);
//        String s = mongoTemplateProxy.updateOneDecisionDetail(decisionResultTable, update, query,requestId,decisionId);
        return 0;
    }
}
